#include "Atomizer.h"

namespace nexus {

Atomizer::Atomizer(Pipeline& pipeline, Network& network)
    : pipeline_(pipeline), network_(network) {}

void
Atomizer::run() {
  while (1) {
    std::shared_ptr<RouteUpdate> dpu;
    auto ticket = pipeline_.blockingReadStage<1>(dpu);
    // XLOG(INFO) << "Got dpu at Atomizer";
    // AtomicOverwrite ao;
    auto ao = std::make_shared<AtomicOverwrite>();
    ao->device = dpu->device;
    process(*dpu, *ao);
    pipeline_.blockingWriteStage<1>(ticket, std::move(ao));
  }
}

void
Atomizer::process(RouteUpdate& dpu, AtomicOverwrite& ao) {
  seq(dpu, ao);
}

// TODO: parallel in this stage may lead to error due to relation between routes
void
Atomizer::par(RouteUpdate& dpu, AtomicOverwrite& ao) {
  auto device = dpu.device;
  std::for_each(
      // std::execution::par,
      std::execution::par_unseq,
      dpu.added.begin(),
      dpu.added.end(),
      [&](auto route) {
        std::vector<std::shared_ptr<Route>> overlapSet;
        device->getOverlapSet(route, overlapSet);
        // XLOG(INFO) << "num overlap: " << overlapSet.size();
        std::vector<HeaderSpace> hss;
        // HeaderSpace fwd;
        for (auto& r : overlapSet) {
          // fwd |= r->hs;
          hss.push_back(r->hs);
        }

        auto fwd = std::reduce(
            std::execution::par_unseq,
            hss.begin(),
            hss.end(),
            HeaderSpace::hsEmpty(),
            [](auto x, auto y) { return x | y; });
        route->hit = route->hs - fwd;
        if (!route->hit.isFalse()) {
          // const std::lock_guard<std::mutex> lock(mutex_);
          ao.overwrites.emplace_back(route->hit, route->port);
        }
      });
}

void
Atomizer::seq(RouteUpdate& dpu, AtomicOverwrite& ao) {
  auto device = dpu.device;

  // all insertion is expanded
  auto& expanded = dpu.added;

  HeaderSpace fwd;
  auto oit = device->routes().begin();
  auto nit = expanded.begin();
  while (nit != expanded.end()) {
    if (fwd.isTrue()) {
      break;
    }
    // skip origin rules with high priorities
    // if (oit != device.rules().end() && oit->second.priority > nit->priority)
    // {
    //   fwd |= oit->second.hs;
    //   oit++;
    //   continue;
    // }
    nit->get()->hit = nit->get()->hs - fwd;
    if (!nit->get()->hit.isFalse()) {
      ao.overwrites.emplace_back(nit->get()->hit, nit->get()->port);
      // ao.overwrites.push_back(std::move(std::make_pair(nit->hit,
      // nit->port))); fwd = fwd | nit->hit;
      fwd |= nit->get()->hit;
    }
    nit++;
  }
}

} // namespace nexus